package org.bt.common.utils;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.EscapeUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bt.common.entity.CryptoBody;
import org.bt.common.entity.KeyPair;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;

/**
 * sm2工具类
 *
 * @author tongw
 * @date 2021/10/21
 */
@Slf4j
public class Sm2Util {

    public static String SM2_JAVA = "01";

    /**
     * SM2公钥加密
     * 适用于POST和PUT请求
     *
     * @param body 请求内容明文
     *
     * @return 密文
     */
    @SneakyThrows(Exception.class)
    public static CryptoBody sm2Encrypt(Object body) {
        return sm2Encrypt(JSONUtil.toJsonStr(body));
    }

    @SneakyThrows(Exception.class)
    public static CryptoBody sm2Encrypt(String body) {
        Assert.notNull(body, "body is required!");
        SM2 sm2 = SmUtil.sm2();
        sm2.setMode(SM2Engine.Mode.C1C3C2);
        KeyPair keyPair = sm2KeyPair(sm2);

        CryptoBody cryptoBody = new CryptoBody();
        cryptoBody.setEncryptContent(sm2.encryptBcd(body, KeyType.PublicKey));
        cryptoBody.setKeyPair(KeyPair.builder().publicKey(keyPair.getPublicKey())
                .privateKey(AESUtil.encrypt(keyPair.getPrivateKey())).build());

        return cryptoBody;
    }

    /**
     * SM2公钥加密
     * 适用于GET和DELETE请求
     *
     * @param params 请求参数串 例如: name=tom&&age=15
     *
     * @return 密文
     */
    public static Map<String, Object> sm2EncryptParams(Map<String, Object> params) {
        Map<String, Object> paramsMap = new HashMap<>(1);
        paramsMap.put("sm2param", JSONUtil.toJsonStr(sm2Encrypt(MapUrlParamsUtil.getUrlParamsByMap(params))));
        return paramsMap;
    }

    /**
     * SM2私钥解密
     *
     * @param cryptoBody cryptoBody
     *
     * @return 内容
     */
    @SneakyThrows(Exception.class)
    public static String sm2Decrypt(CryptoBody cryptoBody) {
        Assert.notNull(cryptoBody, "requestBody is required!");
        SM2 sm2 = SmUtil.sm2(
                AESUtil.decrypt(cryptoBody.getKeyPair().getPrivateKey()),
                cryptoBody.getKeyPair().getPublicKey());

        return EscapeUtil.unescape(
                new String(sm2.decryptFromBcd((cryptoBody.getEncryptContent()), KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8))
        );
    }

    public static KeyPair sm2KeyPair(SM2 sm2) {
        return KeyPair.builder()
                .publicKey(HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false)))
                .privateKey(HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey())))
                .build();
    }
}
